﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Thrift.Protocol;
using Thrift.Transport;
using XXF.BaseService.ServiceCenter.Client.ClientPool;
using XXF.BaseService.ServiceCenter.Client.Provider;
using XXF.BaseService.ServiceCenter.Service.SystemRuntime;
using XXF.BaseService.ServiceCenter.SystemRuntime;

namespace XXF.BaseService.ServiceCenter.Client.RemoteClient
{
    /// <summary>
    /// Thrift远程客户端
    /// </summary>
    public class ThriftRemoteClient : BaseRemoteClient
    {
        private static Type _sdkType = null;//thrift协议sdkm,内存编译后,仅使用一个实例
        private static object _lockSdk = new object();//sdk生成使用的锁
        /// <summary>
        /// 调用方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="methodname"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public override T Call<T>(string methodname, object[] parameters)
        {

            int callCount = 0;
            var clientproviderpool = new ClientProviderPool();//提供者连接池
            var clientprovider = clientproviderpool.Get(this.GetType().Namespace);//取出一个提供者

            while (callCount < 5)
            {
                callCount++;
                var loadBalanceInfo = clientprovider.GetLoadBalanceInfo();//获取一个负载均衡节点
                var clientpoolprovider = new ClientPoolProvider();//客户端连接池
                bool errorflag = false;
                try
                {
                    var clientpool = clientpoolprovider.GetPool<ThriftClientPool>(new BaseClientConfig()
                    {
                        Host = loadBalanceInfo.Host,
                        Port = loadBalanceInfo.Port,
                        ServiceType = SystemRuntime.EnumServiceType.Thrift
                    });//取出一个客户端池

                    var transport = clientpool.BorrowInstance();//从客户端池中取出客户端
                    try
                    {
                        TProtocol protocol = new TBinaryProtocol(transport);
                        var thriftdlltype = GetClientSDKType(clientprovider);
                        var thfriftclienttype = thriftdlltype.Assembly.GetType(thriftdlltype.FullName + "+Client");
                        var client = Activator.CreateInstance(thfriftclienttype, new object[] { protocol });
                        var methodinfo = thfriftclienttype.GetMethod(methodname);
                        //参数转换
                        var methodparamters = methodinfo.GetParameters();
                        object[] ps = new object[parameters.Length];
                        int index = 0;
                        foreach (var p in parameters)
                        {
                            ps[index] = TypeConvertHelper.Convert(methodparamters[index].ParameterType, p);
                            index++;
                        }
                        //兼容void
                        if (methodinfo.ReturnParameter.ParameterType == typeof(void))
                        {
                            methodinfo.Invoke(client, ps);
                            return default(T);
                        }
                        else
                        {
                            var r = methodinfo.Invoke(client, ps);
                            return TypeConvertHelper.Convert<T>(r);
                        }
                    }
                    catch (Exception exp)
                    {
                        XXF.Log.CommLog.Write(transport.IsOpen.ToString());
                        Thread.Sleep(10);
                        errorflag = true;
                        throw exp;
                    }
                    finally
                    {
                        clientpool.ReturnInstance(transport, errorflag);//归还客户端到客户端池
                    }
                }
                ///连接异常
                catch (SocketException ex)
                {
                    clientprovider.RemoveNode(this.GetType().Namespace, loadBalanceInfo.Host, loadBalanceInfo.Port);
                    string msg = string.Format("Socket通信异常,方法{0},客户端类型{1},Host{2}", methodname, this.GetType().Name, loadBalanceInfo.Host);
                    LogHelper.Error(-1, EnumLogType.Client, msg, ex);
                    continue;
                }
                ///连接异常
                catch (IOException ex)
                {
                    clientprovider.RemoveNode(this.GetType().Namespace, loadBalanceInfo.Host, loadBalanceInfo.Port);
                    string msg = string.Format("IO通信异常,方法{0},客户端类型{1},Remote{2}", methodname, this.GetType().Name, loadBalanceInfo.Host);
                    LogHelper.Error(-1, EnumLogType.Client, msg, ex);
                    continue;
                }
                catch (TTransportException ex)
                {
                    clientprovider.RemoveNode(this.GetType().Namespace, loadBalanceInfo.Host, loadBalanceInfo.Port);
                    string msg = string.Format("TimeOut通信异常,方法{0},客户端类型{1},Remote{2}", methodname, this.GetType().Name, loadBalanceInfo.Host);
                    LogHelper.Error(-1, EnumLogType.Client, msg, ex);
                    continue;
                }
                catch (Exception exp)
                {
                    string msg = string.Format("Thrift客户端调用出错,方法{0},客户端类型{1},Remote{2}", methodname, this.GetType().Name, loadBalanceInfo.Host);
                    LogHelper.Error(-1, EnumLogType.Client, msg, exp);
                    return default(T);
                }
            }
            return default(T);

        }

        private Type GetClientSDKType(ClientProvider provider)
        {
            //第一次运行则编译生成thriftsdk
            if (_sdkType == null)
            {
                lock (_lockSdk)
                {
                    if (_sdkType == null)
                    {
                        var protocal = provider.GetServiceProtocal();//获取服务协议
                        string namepsace = this.GetType().Namespace;
                        var thriftclienttype = new ServiceCenter.Service.Protocol.ProtocolsToThrift().ToType(protocal, namepsace);
                        _sdkType = thriftclienttype;
                    }
                }
            }
            return _sdkType;
        }
    }
}
